﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Wise.Util.Queryable
{
    class CacheHelper
    {

        /// <summary>
        /// 默认缓存时间
        /// </summary>
        public static readonly TimeSpan DEFAULT_TIME_OUT = new TimeSpan(0, 20, 0);
        /// <summary>
        ///最大缓存同步等待毫秒数
        /// </summary>
        public const int MAX_WAIT_MILLION_SECOND = 1000;

        private class MyCacheItem
        {
            internal MyCacheItem(Object cachedData, double? relativeMillionSecond, DateTime? absoluteDate)
            {
                CachedData = cachedData;
                CacheMillionSeconds = relativeMillionSecond;
                CacheExpiredDate = absoluteDate;
            }
            private readonly double? CacheMillionSeconds = null;

            private object _CachedData = null;

            internal object CachedData
            {
                get
                {
                    LatestVisitDate = DateTime.Now;
                    return _CachedData;
                }
                set
                {
                    LatestVisitDate = DateTime.Now;
                    _CachedData = value;
                }
            }
            private readonly DateTime? CacheExpiredDate = null;
            private DateTime LatestVisitDate = DateTime.Now;
            private readonly DateTime CreateDate = DateTime.Now;

            internal bool IsExpired
            {
                get
                {
                    return CacheMillionSeconds.HasValue ?
                        (DateTime.Now - LatestVisitDate).TotalMilliseconds > CacheMillionSeconds.Value
                        :
                        (CacheExpiredDate.HasValue ?
                            DateTime.Now > CreateDate
                            :
                            true
                        )
                            ;
                }
            }
        }

        private static readonly Dictionary<string, MyCacheItem> _MyCache = new Dictionary<string, MyCacheItem>();

        /// <summary>
        /// 获取缓存值
        /// </summary>
        /// <typeparam name="TReturn"></typeparam>
        /// <param name="cacheKey"></param>
        /// <param name="onExpired">当缓存过期时，自动获取要缓存值的委托</param>
        /// <param name="relativeExpired"></param>
        /// <param name="monitorFiles">文件监视器列表，当列表中任意文件或文件夹变动时，缓存过期</param>
        /// <returns></returns>
        public static TReturn Get<TReturn>(string cacheKey, Func<TReturn> onExpired, TimeSpan relativeExpired, List<string> monitorFiles = null)
        {
            TReturn returnValue = default(TReturn);
            if (!Monitor.TryEnter(cacheKey, 10000))
            {
                return default(TReturn);
            }
            try
            {
#if DOTNET40 || DOTNET46

                bool flagIsExpied = false;
                if (Default.Contains(cacheKey))
                {
                #region 检查现存缓存
                    try
                    {
                        returnValue = (TReturn)Default.Get(cacheKey);
                        if (returnValue is ICacheItem)
                        {
                            ICacheItem cItem = returnValue as ICacheItem;
                            if (cItem.IsObsolete())
                            {
                                flagIsExpied = true;
                            }
                        }
                    }
                    catch //(Exception exp)
                    {
                        //if (exp is NullReferenceException)
                        //{
                        //    LoggerHelper.Log(exp, LoggerType.THREAD | LoggerType.CACHE);
                        //}
                        //else
                        //{
                        //    LoggerHelper.Log(exp, LoggerType.INVALID_OPERATION | LoggerType.CACHE);
                        //}
                        flagIsExpied = true;
                    }
                    finally
                    {

                    }
                #endregion
                }
                else
                {
                    flagIsExpied = true;
                }
                if (flagIsExpied)
                {
                #region 过期处理
                    if (Default.Contains(cacheKey))
                        Default.Remove(cacheKey);
                    DateTime now = DateTime.Now;
                    if (onExpired != null)
                    {
                        //每用户*缓存项 有5秒钟时间处理缓存
                        //string writeLocker = string.Format("{0}-{1}", SimpleAppHelper.InstanceFromCache.CurrentUserId, cacheKey);
                        //if(Monitor.TryEnter(writeLocker,5000))
                        {
                            //不理会onExpired会返回null值
                            try
                            {
                                returnValue = onExpired();
                            }
                            finally
                            {
                                //Monitor.Exit(writeLocker);
                            }
                            double ms = (DateTime.Now - now).TotalMilliseconds;
                            //if (ms > MAX_WAIT_MILLION_SECOND)
                            //{
                            //    LoggerHelper.Warning(GlobalizationHelper.GetString("获取缓存[{0}]的值使用[{1}]毫秒", cacheKey, ms), LoggerType.EFFICIENT);
                            //}
                            if (returnValue != null)
                            {
                                CacheItem item = new CacheItem(cacheKey, returnValue);
                                CacheItemPolicy policy = new CacheItemPolicy();
                                policy.SlidingExpiration = relativeExpired;
                                if (monitorFiles != null && monitorFiles.Count > 0)
                                {
                                    HostFileChangeMonitor myFM = new HostFileChangeMonitor(monitorFiles);
                                    policy.ChangeMonitors.Add(myFM);
                                }
                                Default.Add(item, policy);
                            }
                        }
                    }
                #endregion
                }
#else
                MyCacheItem item = null;
                if (_MyCache.ContainsKey(cacheKey))
                {
                    if (Monitor.TryEnter(_MyCache, 10))
                    {
                        try
                        {
                            item = _MyCache[cacheKey];
                            if (item.IsExpired)
                            {
                                _MyCache.Remove(cacheKey);
                                item = null;
                            }
                            returnValue = item == null ? default(TReturn) : (TReturn)item.CachedData;
                        }
                        //catch (Exception exp)
                        //{
                            //LoggerHelper.Instance.Log(exp, LoggerType.THREAD);
                        //}
                        finally
                        {
                            Monitor.Exit(_MyCache);
                        }
                    }
                }
                if (item == null)
                {
                    DateTime now = DateTime.Now;
                    if (onExpired != null)
                    {
                        returnValue = onExpired();
                        double ms = (DateTime.Now - now).TotalMilliseconds;
                        if (ms > MAX_WAIT_MILLION_SECOND)
                        {
                            //LoggerHelper.Instance.Warning(GlobalizationHelper.GetString("获取缓存[{0}]的值使用[{1}]毫秒", cacheKey, ms), LoggerType.EFFICIENT);
                        }
                        if (_MyCache.ContainsKey(cacheKey))
                        {
                            _MyCache.Remove(cacheKey);
                        }
                        if (returnValue != null)
                            _MyCache.Add(cacheKey, new MyCacheItem(returnValue, relativeExpired.TotalMilliseconds, null));
                    }
                }
#endif

            }
            finally
            {
                Monitor.Exit(cacheKey);
            }
            return returnValue;
        }



        /// <summary>
        /// 获取缓存值(固定缓存五分钟)
        /// </summary>
        /// <returns></returns>
        public static TReturn Get<TReturn, TPara>(string cacheKey, Func<TPara, TReturn> onExpired, TPara para1)
        {
            TReturn returnValue = default(TReturn);

#if DOTNET40 || DOTNET46
            bool flagIsExpied = false;
            if (Default.Contains(cacheKey))
            {
            #region 检查现存缓存
                try
                {
                    returnValue = (TReturn)Default.Get(cacheKey);
                }
                catch //(Exception exp)
                {
                    //if (exp is NullReferenceException)
                    //{
                    //    LoggerHelper.Log(exp, LoggerType.THREAD | LoggerType.CACHE);
                    //}
                    //else
                    //{
                    //    LoggerHelper.Log(exp, LoggerType.INVALID_OPERATION | LoggerType.CACHE);
                    //}
                    flagIsExpied = true;
                }
            #endregion
            }
            else
            {
                flagIsExpied = true;
            }
            if (flagIsExpied)
            {
            #region 过期处理
                DateTime now = DateTime.Now;
                if (onExpired != null)
                {
                    //不理会onExpired会返回null值
                    returnValue = onExpired(para1);
                    double ms = (DateTime.Now - now).TotalMilliseconds;
                    //if (ms > MAX_WAIT_MILLION_SECOND)
                    //{
                    //    LoggerHelper.Warning(GlobalizationHelper.GetString("获取缓存[{0}]的值使用[{1}]毫秒", cacheKey, ms), LoggerType.EFFICIENT);
                    //}
                    if (returnValue != null)
                    {
                        CacheItem item = new CacheItem(cacheKey, returnValue);
                        CacheItemPolicy policy = new CacheItemPolicy();
                        policy.SlidingExpiration = new TimeSpan(0, 5, 0);
                        if (Default.Contains(cacheKey))
                            Default.Remove(cacheKey);
                        Default.Add(item, policy);
                    }
                }
            #endregion
            }
#else
            MyCacheItem item = null;
            if (_MyCache.ContainsKey(cacheKey))
            {
                if (Monitor.TryEnter(_MyCache, 10))
                {
                    try
                    {
                        item = _MyCache[cacheKey];
                        if (item.IsExpired)
                        {
                            _MyCache.Remove(cacheKey);
                            item = null;
                        }
                        returnValue = item == null ? default(TReturn) : (TReturn)item.CachedData;
                    }
                    //catch (Exception exp)
                    //{
                    //    LoggerHelper.Instance.Log(exp, LoggerType.THREAD);
                    //}
                    finally
                    {
                        Monitor.Exit(_MyCache);
                    }
                }
            }
            if (item == null)
            {
                DateTime now = DateTime.Now;
                if (onExpired != null)
                {
                    returnValue = onExpired(para1);
                    double ms = (DateTime.Now - now).TotalMilliseconds;
                    if (ms > MAX_WAIT_MILLION_SECOND)
                    {
                        //LoggerHelper.Instance.Warning(GlobalizationHelper.GetString("获取缓存[{0}]的值使用[{1}]毫秒", cacheKey, ms), LoggerType.EFFICIENT);
                    }
                    if (_MyCache.ContainsKey(cacheKey))
                    {
                        _MyCache.Remove(cacheKey);
                    }
                    if (returnValue != null)
                        _MyCache.Add(cacheKey, new MyCacheItem(returnValue, 300000, null));
                }
            }
#endif
            return returnValue;
        }
    }
}
